Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


Examples/Ratchets.py 51b7dc7cd4b793ead979dfa8d1ddaeaa2ca0ae61 (51b7dc7c) Text, 12.47 KB

T8b949e##########################################################
T8b949e# This RNS example demonstrates a simple client/server #
T8b949e# echo utility that uses ratchets to rotate encryption #
T8b949e# keys everytime an announce is sent. #
T8b949e##########################################################

Tff7b72import T7ee787argparse
Tff7b72import T7ee787sys
Tff7b72import T7ee787RNS

T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"


T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################

T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3reticulum

T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our echo server
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)

T8b949e# We create a destination that clients can query. We want
T8b949e# to be able to verify echo replies to our clients, so we
T8b949e# create a "single" destination that can receive encrypted
T8b949e# messages. This way the client can send a request and be
T8b949e# certain that no-one else than this destination was able
T8b949e# to read it.
Te6edf3echo_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffratchetTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffechoTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffrequestTa5d6ff"
Tb4b4b4)

T8b949e# Enable ratchets on the destination by providing a file
T8b949e# path to store ratchets. In this example, we will just
T8b949e# use a temporary file, but in real-world applications,
T8b949e# it's extremely important to keep this file secure, since
T8b949e# it contains encryption keys for the destination.
Te6edf3destination_hexhash Tff7b72= Te6edf3RNSTff7b72.Td2a8ffhexrepTb4b4b4(Te6edf3echo_destinationTff7b72.Td2a8ffhashTb4b4b4, Te6edf3delimitTff7b72=Tff7b72FalseTb4b4b4)
Te6edf3echo_destinationTff7b72.Td2a8ffenable_ratchetsTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff/tmp/Tffd700{Te6edf3destination_hexhashTffd700}Ta5d6ff.ratchetsTa5d6ff"Tb4b4b4)

T8b949e# We configure the destination to automatically prove all
T8b949e# packets addressed to it. By doing this, RNS will automatically
T8b949e# generate a proof for each incoming packet and transmit it
T8b949e# back to the sender of that packet.
Te6edf3echo_destinationTff7b72.Td2a8ffset_proof_strategyTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffPROVE_ALLTb4b4b4)
T8b949e# Tell the destination which function in our program to
T8b949e# run when a packet is received. We do this so we can
T8b949e# print a log message when the server receives a request
Te6edf3echo_destinationTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3server_callbackTb4b4b4)

T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3announceLoopTb4b4b4(Te6edf3echo_destinationTb4b4b4)


Tff7b72def Td2a8ffannounceLoopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffRatcheted echo server Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, hit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"
Tb4b4b4)

T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)


Tff7b72def Td2a8ffserver_callbackTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3reticulum
T8b949e# Tell the user that we received an echo request, and
T8b949e# that we are going to send a reply to the requester.
T8b949e# Sending the proof is handled automatically, since we
T8b949e# set up the destination to prove all incoming packets.

Te6edf3reception_stats Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3reticulumTff7b72.Td2a8ffis_connected_to_shared_instanceTb4b4b4:
Te6edf3reception_rssi Tff7b72= Te6edf3reticulumTff7b72.Td2a8ffget_packet_rssiTb4b4b4(Te6edf3packetTff7b72.Td2a8ffpacket_hashTb4b4b4)
Te6edf3reception_snr Tff7b72= Te6edf3reticulumTff7b72.Td2a8ffget_packet_snrTb4b4b4(Te6edf3packetTff7b72.Td2a8ffpacket_hashTb4b4b4)

Tff7b72if Te6edf3reception_rssi Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [RSSI Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3reception_rssiTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72if Te6edf3reception_snr Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [SNR Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3reception_snrTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"

Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3packetTff7b72.Td2a8ffrssi Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [RSSI Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3packetTff7b72.Td2a8ffrssiTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72if Te6edf3packetTff7b72.Td2a8ffsnr Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [SNR Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3packetTff7b72.Td2a8ffsnrTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dB]Ta5d6ff"

Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived packet from echo client, proof sentTa5d6ff"Tff7b72+Te6edf3reception_statsTb4b4b4)


T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################

T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4, Te6edf3timeoutTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3reticulum
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)

Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tff7b72+Ta5d6ff"Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)

T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)

T8b949e# We override the loglevel to provide feedback when
T8b949e# an announce is received
Tff7b72if Te6edf3RNSTff7b72.Td2a8ffloglevel Tff7b72< Te6edf3RNSTff7b72.Td2a8ffLOG_INFOTb4b4b4:
Te6edf3RNSTff7b72.Td2a8ffloglevel Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLOG_INFO

T8b949e# Tell the user that the client is ready!
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffEcho client ready, hit enter to send echo request to Ta5d6ff"Tff7b72+
Te6edf3destination_hexhashTff7b72+
Ta5d6ff"Ta5d6ff (Ctrl-C to quit)Ta5d6ff"
Tb4b4b4)

T8b949e# We enter a loop that runs until the user exits.
T8b949e# If the user hits enter, we will try to send an
T8b949e# echo request to the destination specified on the
T8b949e# command line.
Tff7b72while Tff7b72TrueTb4b4b4:
Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Let's first check if RNS knows a path to the destination.
T8b949e# If it does, we'll load the server identity and create a packet
Tff7b72if Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:

T8b949e# To address the server, we need to know it's public
T8b949e# key, so we check if Reticulum knows this destination.
T8b949e# This is done by calling the "recall" method of the
T8b949e# Identity module. If the destination is known, it will
T8b949e# return an Identity instance that can be used in
T8b949e# outgoing destinations.
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)

T8b949e# We got the correct identity instance from the
T8b949e# recall method, so let's create an outgoing
T8b949e# destination. We use the naming convention:
T8b949e# example_utilities.ratchet.echo.request
T8b949e# This matches the naming we specified in the
T8b949e# server part of the code.
Te6edf3request_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffratchetTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffechoTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffrequestTa5d6ff"
Tb4b4b4)

T8b949e# The destination is ready, so let's create a packet.
T8b949e# We set the destination to the request_destination
T8b949e# that was just created, and the only data we add
T8b949e# is a random hash.
Te6edf3echo_request Tff7b72= Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3request_destinationTb4b4b4, Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffget_random_hashTb4b4b4(Tb4b4b4)Tb4b4b4)

T8b949e# Send the packet! If the packet is successfully
T8b949e# sent, it will return a PacketReceipt instance.
Te6edf3packet_receipt Tff7b72= Te6edf3echo_requestTff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)

T8b949e# If the user specified a timeout, we set this
T8b949e# timeout on the packet receipt, and configure
T8b949e# a callback function, that will get called if
T8b949e# the packet times out.
Tff7b72if Te6edf3timeout Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3packet_receiptTff7b72.Td2a8ffset_timeoutTb4b4b4(Te6edf3timeoutTb4b4b4)
Te6edf3packet_receiptTff7b72.Td2a8ffset_timeout_callbackTb4b4b4(Te6edf3packet_timed_outTb4b4b4)

T8b949e# We can then set a delivery callback on the receipt.
T8b949e# This will get automatically called when a proof for
T8b949e# this specific packet is received from the destination.
Te6edf3packet_receiptTff7b72.Td2a8ffset_delivery_callbackTb4b4b4(Te6edf3packet_deliveredTb4b4b4)

T8b949e# Tell the user that the echo request was sent
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent echo request to Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3request_destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
Tff7b72elseTb4b4b4:
T8b949e# If we do not know this destination, tell the
T8b949e# user to wait for an announce to arrive.
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually retry once an announce is received.Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)

T8b949e# This function is called when our reply destination
T8b949e# receives a proof packet.
Tff7b72def Td2a8ffpacket_deliveredTb4b4b4(Te6edf3receiptTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3reticulum

Tff7b72if Te6edf3receiptTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffPacketReceiptTff7b72.Td2a8ffDELIVEREDTb4b4b4:
Te6edf3rtt Tff7b72= Te6edf3receiptTff7b72.Td2a8ffget_rttTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3rtt Tff7b72>Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4:
Te6edf3rtt Tff7b72= Tffa657roundTb4b4b4(Te6edf3rttTb4b4b4, T79c0ff3Tb4b4b4)
Te6edf3rttstring Tff7b72= Tffa657strTb4b4b4(Te6edf3rttTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff secondsTa5d6ff"
Tff7b72elseTb4b4b4:
Te6edf3rtt Tff7b72= Tffa657roundTb4b4b4(Te6edf3rttTff7b72*T79c0ff1000Tb4b4b4, T79c0ff3Tb4b4b4)
Te6edf3rttstring Tff7b72= Tffa657strTb4b4b4(Te6edf3rttTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff millisecondsTa5d6ff"

Te6edf3reception_stats Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3reticulumTff7b72.Td2a8ffis_connected_to_shared_instanceTb4b4b4:
Te6edf3reception_rssi Tff7b72= Te6edf3reticulumTff7b72.Td2a8ffget_packet_rssiTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffpacket_hashTb4b4b4)
Te6edf3reception_snr Tff7b72= Te6edf3reticulumTff7b72.Td2a8ffget_packet_snrTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffpacket_hashTb4b4b4)

Tff7b72if Te6edf3reception_rssi Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [RSSI Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3reception_rssiTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72if Te6edf3reception_snr Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [SNR Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3reception_snrTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dB]Ta5d6ff"

Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffproof_packet Tff7b72!= Tff7b72NoneTb4b4b4:
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffrssi Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [RSSI Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffrssiTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffsnr Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [SNR Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffsnrTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dB]Ta5d6ff"

Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffValid reply received from Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffdestinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff, round-trip time is Ta5d6ff"Tff7b72+Te6edf3rttstringTff7b72+
Te6edf3reception_stats
Tb4b4b4)

T8b949e# This function is called if a packet times out.
Tff7b72def Td2a8ffpacket_timed_outTb4b4b4(Te6edf3receiptTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffPacketReceiptTff7b72.Td2a8ffFAILEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffPacket Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff timed outTa5d6ff"Tb4b4b4)


T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################

T8b949e# This part of the program gets run at startup,
T8b949e# and parses input from the user, and then starts
T8b949e# the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple ratcheted echo server and client utilityTa5d6ff"Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming packets from clientsTa5d6ff"
Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-tTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--timeoutTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3metavarTff7b72=Ta5d6ff"Ta5d6ffsTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffset a reply timeout in secondsTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657float
Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)

Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)

Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3configargTff7b72=Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None

Tff7b72if Te6edf3argsTff7b72.Td2a8fftimeoutTb4b4b4:
Te6edf3timeoutarg Tff7b72= Tffa657floatTb4b4b4(Te6edf3argsTff7b72.Td2a8fftimeoutTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3timeoutarg Tff7b72= Tff7b72None

Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4, Te6edf3timeoutTff7b72=Te6edf3timeoutargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────